using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace LS.Samples
{
    public class Unit2DManager : MonoBehaviour
    {
        public bool positionRoundToInt = true;
        public bool sizeRoundToInt = true;
        private readonly Dictionary<int, SpriteRenderer> m_Units = new Dictionary<int, SpriteRenderer>();
        private readonly List<Vector2Int> m_TempPositionList = new List<Vector2Int>();
        private int m_UnitIndex = 0;

        public int UnitCount
        {
            get { return m_Units.Count; }
        }

        public int CreateUnit(Bounds bound, Sprite sprite)
        {
            Vector2 pivot = sprite.pivot / sprite.rect.size;
            Vector3 finalPosition = bound.min;
            finalPosition.z = (bound.max.z + bound.min.z) * 0.5f;
            Vector2 finalSize = bound.size;

            if (positionRoundToInt)
            {
                finalPosition = Vector3Int.RoundToInt(finalPosition);
            }

            if (sizeRoundToInt)
            {
                finalSize = Vector2Int.RoundToInt(finalSize);
            }

            int id = ++m_UnitIndex;
            GameObject unitObject = new GameObject($"Unit_{id}");
            unitObject.transform.SetParent(transform);
            SpriteRenderer unitSpriteRenderer = unitObject.AddComponent<SpriteRenderer>();
            unitSpriteRenderer.sprite = sprite;
            unitSpriteRenderer.drawMode = SpriteDrawMode.Sliced;
            unitSpriteRenderer.size = finalSize;
            unitObject.transform.position =
                finalPosition + new Vector3(finalSize.x * pivot.x, finalSize.y * pivot.y, 0);
            m_Units.Add(id, unitSpriteRenderer);
            return id;
        }

        public SpriteRenderer GetUnit(int unitId)
        {
            if (m_Units.TryGetValue(unitId, out SpriteRenderer unitSpriteRenderer))
            {
                return unitSpriteRenderer;
            }

            return null;
        }

        public void DeleteUnit(int unitId)
        {
            if (m_Units.TryGetValue(unitId, out SpriteRenderer unitSpriteRenderer))
            {
                GameObject.Destroy(unitSpriteRenderer.gameObject);
                m_Units.Remove(unitId);
            }
        }

        public bool CastUnit(Rect rect, out Vector2Int[] unitPosition, out bool[,] testResult)
        {
            m_TempPositionList.Clear();
            Rect[,] rects = GetMapBlockRects(rect);

            int xCount = rects.GetLength(0);
            int yCount = rects.GetLength(1);
            var keys = m_Units.Keys.ToArray();
            testResult = new bool[xCount, yCount];
            for (int y = 0; y < yCount; y++)
            {
                for (int x = 0; x < xCount; x++)
                {
                    Rect blockRect = rects[x, y];
                    bool castUnit = false;
                    for (int i = 0; i < keys.Length; i++)
                    {
                        int key = keys[i];
                        Rect unitRect = GetRect(m_Units[key]);
                        if (unitRect.Overlaps(blockRect))
                        {
                            castUnit = true;
                            m_TempPositionList.Add(Vector2Int.RoundToInt(blockRect.min));
                            break;
                        }
                    }

                    testResult[x, y] = castUnit;
                }
            }

            unitPosition = m_TempPositionList.ToArray();
            m_TempPositionList.Clear();
            return unitPosition.Length > 0;
        }

        private Rect GetRect(SpriteRenderer unitSpriteRenderer)
        {
            var bound = unitSpriteRenderer.bounds;
            Rect rect = new Rect(bound.min, bound.size);
            return rect;
        }

        private Rect[,] GetMapBlockRects(Rect rect)
        {
            Vector2Int size = Vector2Int.RoundToInt(rect.size);
            Vector2Int min = Vector2Int.RoundToInt(rect.min);

            Rect[,] rects = new Rect[size.x, size.y];
            for (int y = 0; y < size.y; y++)
            {
                for (int x = 0; x < size.x; x++)
                {
                    Vector2Int tileBlockPosition = min + new Vector2Int(x, y);
                    Rect blockRect = new Rect(tileBlockPosition, Vector2.one);
                    rects[x, y] = blockRect;
                }
            }

            return rects;
        }
    }
}